#include <stdio.h>
#include <stdlib.h>
#include <string.h>


// #include "ds18b20.h"
#include "timer.h"
#include "tick.h"
#include "DigitalTempSensor.h"


//------ DS18B20 Command define
#define DS_CMD_READ_ROM		0x33
#define DS_CMD_MATCH_ROM	0x55
#define DS_SEARCH_ROM		0xf0
#define DS_CMD_SKIP_ROM		0xcc
#define DS_CMD_START_CON	0x44
#define	DS_CMD_READ_RAM		0xbe
#define DS_CMD_WRITE_RAM	0x4e

#define DIGITAL_DEFAULT_READ_PERIODMS	800 
#define ds_delay_us(TIMEINUS)			delay_us(TIMEINUS)


typedef struct {
	int isOpen;
	volatile uint32_t *CRx;
	volatile uint32_t *BSRR;
	volatile uint32_t *BRR;
	volatile uint32_t *IDR;
	uint16_t PinMask;
	uint16_t PinAbsShift;
	int ReadPeriod;
	unsigned int LastReadTime;
	GPIO_TypeDef *PullupCtrlPort;
	uint16_t PullupCtrlPinMask;
} DigitalTempSensor_t;


static int ds18b20_reset(DigitalTempSensor_t *handle);
static int ds18b20_start_con(DigitalTempSensor_t *handle);
static int ds18b20_read_temp(DigitalTempSensor_t *handle);


static DigitalTempSensor_t DigitalTempSensor[PHYSENSOR_CHANNELS];

/*
** @ Open Digital temperature sensor
**/
int OpenDigitalTemperatureSensor(int channel, const SensorChannelHw_t *Hw, int ReadPeriodInMs) {
	DigitalTempSensor_t *Sensor;
	GPIO_InitTypeDef GPIO_InitStructure;
	int i;
	int IOPinNum;
	
	Sensor = &DigitalTempSensor[channel];
	
	if(Sensor->isOpen == TRUE) {
		CloseDigitalTemperatureSensor(channel, Hw);
	}
	
	// Config The IO output at first, PORTB as used
	GPIO_InitStructure.GPIO_Pin = Hw->DataLineB.PinMask;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(Hw->DataLineB.Port, &GPIO_InitStructure);
	
	// Enable the pull-up control port
	GPIO_InitStructure.GPIO_Pin = Hw->PowerPullupCtrl.PinMask;
	GPIO_Init(Hw->PowerPullupCtrl.Port, &GPIO_InitStructure);
	/* Enable Pull-up power for PNP logic */
	GPIO_ResetBits(Hw->PowerPullupCtrl.Port, Hw->PowerPullupCtrl.PinMask);
	
	// Those Pins do not used for digital sensor
	GPIO_InitStructure.GPIO_Pin = Hw->DataLineA.PinMask;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(Hw->DataLineA.Port, &GPIO_InitStructure);
	// Reserved for Hardware V2.0
	if(Hw->Power5VCtrl.Port) {
		GPIO_InitStructure.GPIO_Pin = Hw->Power5VCtrl.PinMask;
		GPIO_Init(Hw->Power5VCtrl.Port, &GPIO_InitStructure);
	}
	
	Sensor->PullupCtrlPort = Hw->PowerPullupCtrl.Port;
	Sensor->PullupCtrlPinMask = Hw->PowerPullupCtrl.PinMask;
	
	Sensor->BSRR = &(Hw->DataLineB.Port->BSRR);
	Sensor->BRR = &(Hw->DataLineB.Port->BRR);
	Sensor->IDR = &(Hw->DataLineB.Port->IDR);
	Sensor->PinMask = Hw->DataLineB.PinMask;
	
	IOPinNum = 0;
	// From Pin Mask to Pin Index Number
	for(i = 0; i < 16; i ++) {
		if(Sensor->PinMask & (1 << i)) {
			break;
		} else {
			IOPinNum ++;
		}
	}
	// from Pin number to CRL or CRH
	if (IOPinNum < 8) {
		Sensor->CRx = &(Hw->DataLineB.Port->CRL);
		// shift = pin_number * 4bit
		Sensor->PinAbsShift = IOPinNum << 2;
	} else {
		Sensor->CRx = &(Hw->DataLineB.Port->CRH);
		// shift = pin_number * 4bit
		Sensor->PinAbsShift = (IOPinNum - 8) << 2;
	}	
	
	Sensor->LastReadTime = 0;
	Sensor->ReadPeriod = ReadPeriodInMs;
	if (Sensor->ReadPeriod == -1) {
		Sensor->ReadPeriod = DIGITAL_DEFAULT_READ_PERIODMS;
	}
	
	if(ds18b20_reset(Sensor) != 0) {
		// ds_set_dat_input(Sensor);
		// free(Sensor);
		CloseDigitalTemperatureSensor(channel, Hw);
		return -1;
	} else {
		Sensor->isOpen = TRUE;
		return 0;
	}
}

/*
** @ Close digital temperature sensor
**/
void CloseDigitalTemperatureSensor(int channel, const SensorChannelHw_t *Hw) {
	DigitalTempSensor_t *Sensor;
	GPIO_InitTypeDef GPIO_InitStructure;
	
	Sensor = &DigitalTempSensor[channel];
	// ds_set_dat_input(Sensor);
	if(DigitalTempSensor[channel].isOpen == FALSE) {
		return;
	}
	
	// Reset Pullup Control Pin
	GPIO_InitStructure.GPIO_Pin = Sensor->PullupCtrlPinMask;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(Sensor->PullupCtrlPort, &GPIO_InitStructure);
	
	// Reset Data-line
	GPIO_InitStructure.GPIO_Pin = Hw->DataLineB.PinMask;
	GPIO_Init(Hw->DataLineB.Port, &GPIO_InitStructure);
	
	Sensor->isOpen = FALSE;
}

/*
** @ Read digital temperature sensor, return 1 for success, 0 for skip
**/
int ReadDigitalTemperatureSensor(int channel, uint16 *TempValue16) {
	DigitalTempSensor_t *handle;
	int buffer;
	
	handle = &DigitalTempSensor[channel];
	if(handle->isOpen == FALSE) {
		return 0;
	}
	
	if (handle->LastReadTime == 0) {
		ds18b20_start_con(handle);
		handle->LastReadTime = get_tick_count();
		return 0;
	} else {
		if ( (get_tick_count() - handle->LastReadTime) > handle->ReadPeriod ) {
			handle->LastReadTime = get_tick_count();
			buffer = ds18b20_read_temp(handle);
			ds18b20_start_con(handle);
			if(buffer != -1) {
				*TempValue16 = (uint16_t)buffer;
				return 1;
			}				
		}		
	}
	return 0;
}

/*
** @ Set read period in millisecond
**/
int SetDigitalTemperatureSensorReadPeriodInMs(int channel, int MilliSecond) {
	if(DigitalTempSensor[channel].isOpen == FALSE) {
		return -1;
	}
	
	if(MilliSecond < 500) {
		return -1;
	}
	DigitalTempSensor[channel].ReadPeriod = MilliSecond;
	return 0;
}

int DigitalSensorMain() {
	int ret = 0;
	uint16_t temp16;
	double temperature;
	
	//handle = OpenDigitalTemperatureSensor(GPIOB, GPIO_Pin_12, -1);
	ret += OpenDigitalTemperatureSensor(0, &channelHwInfo[0], -1);
	ret += OpenDigitalTemperatureSensor(1, &channelHwInfo[1], -1);
	ret += OpenDigitalTemperatureSensor(2, &channelHwInfo[2], -1);
	if(ret == 0) {
		while (1) {
			if(ReadDigitalTemperatureSensor(0, &temp16)) {
				temperature = (double)temp16 / 16.0;
				// temperature = temperature;
				if (temperature < 0) {
					break;
				}
			}
			if(ReadDigitalTemperatureSensor(1, &temp16)) {
				temperature = (double)temp16 / 16.0;
				// temperature = temperature;
				if (temperature < 0) {
					break;
				}
			}
			if(ReadDigitalTemperatureSensor(2, &temp16)) {
				temperature = (double)temp16 / 16.0;
				// temperature = temperature;
				if (temperature < 0) {
					break;
				}
			}
		}
		return 0;
	} else {
		return -1;
	}
}

//*************************************************************************
//*					DS18B20 Lowlevel Function Driver
//*************************************************************************

static __inline void ds_set_dat_output(DigitalTempSensor_t *handle) {
	*handle->CRx &= ~(0xF << handle->PinAbsShift);
	*handle->CRx |= (0x3 << handle->PinAbsShift);
	*handle->BSRR = handle->PinMask;
}

static __inline void ds_set_dat(DigitalTempSensor_t *handle) {
	*handle->BSRR = handle->PinMask;
}

static __inline void ds_clear_dat(DigitalTempSensor_t *handle) {
	*handle->BRR = handle->PinMask;
}

static __inline void ds_set_dat_input(DigitalTempSensor_t *handle) {
	*handle->CRx &= ~(0xF << handle->PinAbsShift);
	*handle->CRx |= (0x8 << handle->PinAbsShift);
	*handle->BSRR = handle->PinMask;
}

static int __inline ds_get_dat(DigitalTempSensor_t *handle) {
	return ((*handle->IDR) & (handle->PinMask)) ? 1 : 0;
}


static int ds18b20_reset(DigitalTempSensor_t *handle) {
	unsigned int buf = 0;	

	ds_set_dat_output(handle);
	
	// Start with H state, End with H state
	ds_set_dat(handle);
	ds_delay_us(10);
	
	// Set data = L at least 480 us 
	ds_clear_dat(handle);
	ds_delay_us(600);
	
	// Set dat = H, wait 80us
	ds_set_dat(handle);
	ds_delay_us(80);
	
	// DS18B20 should return a L state here
	ds_set_dat_input(handle);
	buf = ds_get_dat(handle);
	
	// End with H state
	ds_set_dat_output(handle);
	ds_set_dat(handle);
	ds_delay_us(600);
	
	if(buf) {
		return -1;	
	}
	return 0;
}
 
static void ds18b20_write_bit(DigitalTempSensor_t *handle, unsigned char bit) {
	
	ds_set_dat_output(handle);
	
	// Start with H state, End with H state
	ds_set_dat(handle);
	ds_delay_us(10);
	
	// Clear dat, hold for 5us ~ as start condition 
	ds_clear_dat(handle);
	ds_delay_us(5);
	
	if(bit) {
		// write a H
		ds_set_dat(handle);	
	}
	
	// hold for 60, DS18B20 will read bus state here
	ds_delay_us(100); 	 
	
	// End with H state
	ds_set_dat(handle);
	ds_delay_us(10);
}

static uint8 ds18b20_read_bit(DigitalTempSensor_t *handle) {
	uint8 buf;
	
	ds_set_dat_output(handle);
	
	// Start with H state, End with H state
	ds_set_dat(handle);
	ds_delay_us(10);
	
	// Clear dat, hold for 5us ~ as start condition 
	ds_clear_dat(handle);
	ds_delay_us(5);
	ds_set_dat(handle);
	
	// Data will be read at 10us after  
	ds_set_dat_input(handle);
	ds_delay_us(5);
	
	buf = ds_get_dat(handle);
	
	ds_delay_us(50);
	ds_set_dat_output(handle);
	
	// End with H state
	ds_set_dat(handle);
	ds_delay_us(10);
	
	return buf;
}

static void ds18b20_write_byte(DigitalTempSensor_t *handle, unsigned char c) {
	int i;
	
	for(i = 0; i < 8; i ++) {
		// LSB first
		ds18b20_write_bit(handle, (c >> i) & 0x01);
	}	
}

static int ds18b20_read_byte(DigitalTempSensor_t *handle) {
	int i = 0;
	uint8 buf = 0;
	
	for(i = 0; i < 8; i ++) {
		buf >>= 1;
		// LSB First	
		if(ds18b20_read_bit(handle)) {
			buf |= 0x80;	
		}
	}
	return buf;	
}

static int ds18b20_start_con(DigitalTempSensor_t *handle) {
	if(handle == NULL) {
		return -1;	
	}
	// reset first
	if(ds18b20_reset(handle) < 0) {
		return -1;	
	}
	ds18b20_write_byte(handle, DS_CMD_SKIP_ROM);
	ds18b20_write_byte(handle, DS_CMD_START_CON);
	return 0;
}

static int ds18b20_read_temp(DigitalTempSensor_t *handle) {
	uint8_t buffer[2];
	
	if(handle == NULL) {
		return -1;	
	}	
	
	// reset DS18B20
	if(ds18b20_reset(handle) < 0) {
		return -1;	
	}
	
	ds18b20_write_byte(handle, DS_CMD_SKIP_ROM);
	ds18b20_write_byte(handle, DS_CMD_READ_RAM);
	buffer[0] = ds18b20_read_byte(handle);
	buffer[1] = ds18b20_read_byte(handle);
	
	return buffer[1] << 8 | buffer[0];
}
